package MaquinaVirtual;
import java.util.ArrayList;
import java.util.Stack;
import java.util.List;
import java.io.FileInputStream;
import java.io.ObjectInputStream;

public class TinyVM {
	private final static int MAX = 1000;
	private Stack<PValue> pilaEvaluacion;
	private Instruccion[] programa;
	private int cp;

	private PValue[] memoria = new PValue[MAX];
	private int dirheap = MAX;
	public void setDirheap(int dirheap) {
		this.dirheap = dirheap;
	}

	public int getDirheap() {
		return dirheap;
	}

	private int maxDisplay;

	// para una máquina más complicada existirá también una memoria de
	// evaluación. Puede representarse, por ejemplo, mediante un array,
	// o mediante Vector (aunque Vector, como Stack, suponen una carga
	// importante para el programa).

	public static abstract class PValue {
		public int asInt() {
			throw new UnsupportedOperationException("asInt");
		}

		public boolean asBool() {
			throw new UnsupportedOperationException("asBool");
		}
		// puede introducirse un nuevo método para cada tipo de valor basico de
		// la
		// maquina P
	}

	// Puede proporcionarse una subclase de PValue para cada tipo de valor
	// basico
	// de la máquina P.

	// Enteros
	public static class IntPValue extends PValue {
		private int value;

		public IntPValue(int value) {
			this.value = value;
		}

		public int asInt() {
			return value;
		}

		public String toString() {
			return "<" + value + ">";
		}
	}

	// Boolean
	public static class BoolPValue extends PValue {
		private boolean value;

		public BoolPValue(boolean value) {
			this.value = value;
		}

		public boolean asBool() {
			return value;
		}

		public String toString() {
			return "<" + value + ">";
		}
	}
	
	public TinyVM(String fprograma) {
		try {
			/*IntPValue p = new IntPValue(0);
			memoria[0]=p;
			IntPValue p1 = new IntPValue(2);
			memoria[1]=p1;*/
			/*IntPValue p2 = new IntPValue(1);
			memoria[4]=p2;
			IntPValue p3 = new IntPValue(2);
			memoria[5]=p3;
			IntPValue p4 = new IntPValue(3);
			memoria[6]=p4;
			IntPValue p5 = new IntPValue(4);
			memoria[7]=p5;*/
			
			pilaEvaluacion = new Stack<PValue>();
			List<Instruccion> instrucciones = (List<Instruccion>) new ObjectInputStream(new FileInputStream(fprograma)).readObject();
			
									
			programa = new Instruccion[instrucciones.size()];
			int j = 0;
			for (Instruccion i : instrucciones){
				programa[j++] = i;
				}
			cp = 0;
		} catch (Exception e) {
			System.err.println("Error al cargar el programa:" + e);
			System.exit(1);
		}
	}

	public void run(boolean traza) {
		InicializaMem();
		if (traza)
		
		
			System.out.println(pilaEvaluacion);
		
		while (cp < programa.length) {
			if (traza)
				System.out.print(programa[cp] + "=>");
			    programa[cp].ejecuta(this);
			if (traza){
				System.out.println(pilaEvaluacion);
			for (int i=0; i<11; i++){
				System.out.print(memoria[i].toString());
			}
			System.out.println();
			}
		}
	}

	
	
	
	//----------FUNCIONES DE MEMORIA----------------//
	public void push(PValue value) {
		pilaEvaluacion.push(value);
	}

	public PValue pop() {
		return pilaEvaluacion.pop();
	}

	
	public PValue peek() {
		return pilaEvaluacion.peek();
	}
	
	public void incCP() {
		cp++;
	}

	public int getCp() {
		return cp;
	}

	public  void setCp(int cp) {
		this.cp = cp;
	}

	public void setMem(int pos, PValue val) {
		memoria[pos]=val;
	}
	
	public PValue getMem(int pos) {
		return memoria[pos];
	}

	public void InicializaMem(){
			for (int i=0;i<MAX;i++){
				IntPValue val = new IntPValue(0);
				memoria[i]=val;
			}
	}	
	public static void main(String args[]) {
		if (args.length != 1) {
			System.err.println("ERROR - debe indicarse archivo a interpretar");
			System.exit(1);
		}
		
		
		TinyVM vm = new TinyVM(args[0]);
		vm.run(true);
	}
}
